#include <sys/stat.h>
#include <cmath>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <vector>

#define SUCCESS 0
#define FAILED 1

#define INFO_LOG(fmt, ...) std::cout << "[INFO] " << fmt << std::endl
#define WARN_LOG(fmt, ...) std::cout << "[WARN] " << fmt << std::endl
#define ERROR_LOG(fmt, ...) std::cerr << "[ERROR] " << fmt << std::endl

using aclFloat16 = uint16_t;  // 模拟半精度浮点类型

// 半精度浮点转换工具
union Float16Converter {
   uint16_t i;
   struct {
      uint16_t mantissa : 10;
      uint16_t exponent : 5;
      uint16_t sign : 1;
   } parts;
};

float float16ToFloat(aclFloat16 h) {
   Float16Converter conv;
   conv.i = h;

   if (conv.parts.exponent == 0) {
      return (conv.parts.sign ? -1.0f : 1.0f) *
             std::ldexp(static_cast<float>(conv.parts.mantissa), -24);
   }

   return (conv.parts.sign ? -1.0f : 1.0f) *
          std::ldexp(1.0f + static_cast<float>(conv.parts.mantissa) / 1024.0f,
                     conv.parts.exponent - 15);
}

aclFloat16 floatToFloat16(float f) {
   Float16Converter conv;
   conv.i = 0;

   if (f == 0.0f)
      return 0;

   conv.parts.sign = (f < 0);
   f = std::fabs(f);

   int exponent;
   float mantissa = std::frexp(f, &exponent);
   exponent += 14;  // 调整偏移量

   if (exponent > 31) {  // 溢出
      exponent = 31;
      mantissa = 1.0f;
   } else if (exponent < 0) {  // 下溢
      exponent = 0;
      mantissa = 0.0f;
   }

   conv.parts.exponent = static_cast<uint16_t>(exponent);
   conv.parts.mantissa = static_cast<uint16_t>((mantissa - 0.5f) * 2048.0f);

   return conv.i;
}

bool ReadFile(const std::string& filePath, std::vector<aclFloat16>& buffer) {
   std::ifstream file(filePath, std::ios::binary);
   if (!file.is_open()) {
      ERROR_LOG("Failed to open file: " << filePath);
      return false;
   }

   file.seekg(0, std::ios::end);
   size_t fileSize = file.tellg();
   file.seekg(0, std::ios::beg);

   if (fileSize % sizeof(aclFloat16) != 0) {
      ERROR_LOG("File size is not aligned with aclFloat16");
      return false;
   }

   size_t numElements = fileSize / sizeof(aclFloat16);
   buffer.resize(numElements);
   file.read(reinterpret_cast<char*>(buffer.data()), fileSize);
   return file.good();
}

bool WriteFile(const std::string& filePath, const std::vector<aclFloat16>& buffer) {
   std::ofstream file(filePath, std::ios::binary);
   if (!file.is_open()) {
      ERROR_LOG("Failed to open file for writing: " << filePath);
      return false;
   }

   file.write(reinterpret_cast<const char*>(buffer.data()), buffer.size() * sizeof(aclFloat16));
   return file.good();
}

int64_t GetShapeSize(const std::vector<int64_t>& shape) {
   int64_t shapeSize = 1;
   for (auto i : shape) {
      shapeSize *= i;
   }
   return shapeSize;
}

int main() {
   // 输入输出形状定义
   const std::vector<int64_t> shape = {100, 100};
   const size_t elementCount = GetShapeSize(shape);

   // 准备输入输出数据缓冲区
   std::vector<aclFloat16> inputData(elementCount);
   std::vector<aclFloat16> inputX1(elementCount);
   std::vector<aclFloat16> inputX2(elementCount);
   std::vector<aclFloat16> inputValue(1);
   std::vector<aclFloat16> outputY(elementCount);

   // 创建输入输出目录
   mkdir("input");
   mkdir("output");

   // 读取输入数据
   if (!ReadFile("input/input_data.bin", inputData)) {
      return FAILED;
   }
   if (!ReadFile("input/input_x1.bin", inputX1)) {
      return FAILED;
   }
   if (!ReadFile("input/input_x2.bin", inputX2)) {
      return FAILED;
   }
   if (!ReadFile("input/input_value.bin", inputValue)) {
      return FAILED;
   }
   INFO_LOG("Input data loaded successfully");

   // 获取标量值
   const float value = float16ToFloat(inputValue[0]);

   // 执行Addcdiv计算: output = input_data + value * (input_x1 / input_x2)
   for (size_t i = 0; i < elementCount; ++i) {
      const float data = float16ToFloat(inputData[i]);
      const float x1 = float16ToFloat(inputX1[i]);
      const float x2 = float16ToFloat(inputX2[i]);

      // 避免除以零
      const float denominator = (x2 == 0.0f) ? 1e-10f : x2;
      const float result = data + value * (x1 / denominator);

      outputY[i] = floatToFloat16(result);
   }

   // 写入输出数据
   if (!WriteFile("output/output_y.bin", outputY)) {
      return FAILED;
   }
   INFO_LOG("Addcdiv operation completed successfully");

   return SUCCESS;
}